package com.baidu.fsg.uid.impl.reuseworkerid;

import com.baidu.fsg.uid.BitsAllocator;
import com.baidu.fsg.uid.impl.DefaultUidGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ReuseWorkerIdDefaultUidGenerator extends DefaultUidGenerator {
    private static final Logger LOGGER = LoggerFactory.getLogger(ReuseWorkerIdDefaultUidGenerator.class);
    /*
    怎样确定本次启动是新建WorkerID还是回收利用旧的WorkerID。
    简单。设置两个WorkID阈值,这里称作w1,w2, 当表中最大WorkID小于w1时一律新建WorkerID;
    当表中最大WorkID处于w1~w2之间时掷硬币方式确定是新建WorkerID还是回收利用旧的WorkerID;
    当表中最大WorkID大于w2时一律回收利用旧的WorkerID;
     */

    protected Integer maxNoBeatDay =100;
    protected Long workerLevel1;
    protected Long workerLevel2;


    @Override
    public void afterPropertiesSet() throws Exception {
        // initialize bits allocator
        bitsAllocator = new BitsAllocator(timeBits, workerBits, seqBits);

        autoWorkerLevel(workerBits);

        // initialize worker id
        workerId = workerIdAssigner.assignReuseWorkerId(workerLevel1, workerLevel2, bitsAllocator.getMaxWorkerId(),maxNoBeatDay);
        if (workerId > bitsAllocator.getMaxWorkerId()) {
            throw new RuntimeException("Worker id " + workerId + " exceeds the max " + bitsAllocator.getMaxWorkerId());
        }

        LOGGER.info("Initialized bits(1, {}, {}, {}) for workerID:{}", timeBits, workerBits, seqBits, workerId);
    }

    private void autoWorkerLevel(int workerBits) {
        // 用户自己设置了就不自动设置
        if (null != workerLevel1 && null != workerLevel2) {
            return;
        }

        /*
        w1,w2设置建议
        为了留出足够回收利用旧的WorkerID空间,w1至少需要大于实际节点数,再加上预留节点扩容空间,建议是1.5倍到3倍之间;
        w2与w1之间差值至少需要大于实际节点数,再加上预留节点扩容空间,建议是1.5倍到3倍之间;
        w2与最大WorkID之间建议留些空间供以后调整，空间建议实际节点数的0.5倍到1倍。
        故建议w2最小值：(1.5+1.5)倍实际节点数,建议w2最大值：(3+3)倍实际节点数,
        故建议WorkID最小值：(1.5+1.5+0.5)倍实际节点数,建议WorkID最大值：(3+3+1)倍实际节点数。
         */
        // 按 w1:1.5倍实际节点数,w2：3倍实际节点数，w2与最大WorkID之间空间:0.5倍实际节点数来反推w1和w2
        long worker = bitsAllocator.getMaxWorkerId() / 5;
        workerLevel1 = worker * 3 / 2;
        workerLevel2 = worker * 3 ;

    }


    public void setWorkerLevel1(Long workerLevel1) {
        this.workerLevel1 = workerLevel1;
    }

    public void setWorkerLevel2(Long workerLevel2) {
        this.workerLevel2 = workerLevel2;
    }

    public void setMaxNoBeatDay(Integer maxNoBeatDay) {
        this.maxNoBeatDay = maxNoBeatDay;
    }
}
